{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Thick line geometry"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Three.js has some example code for thick lines via an instance-based geometry. Since WebGL does not guarantee support for line thickness greater than 1 for GL lines, pytheejs includes these objects by default."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pythreejs import *\n",
    "from IPython.display import display\n",
    "from ipywidgets import VBox, HBox, Checkbox, jslink\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Reduce repo churn for examples with embedded state:\n",
    "from pythreejs._example_helper import use_example_model_ids\n",
    "use_example_model_ids()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First, let's set up a normal GL line for comparison. Depending on your OS/browser combination, this might not respect the `linewidth` argument. E.g. most browsers on Windows does not support linewidth greater than 1, due to lack of support in the ANGLE library that most browsers rely on."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "pythree_example_model_006",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Preview(child=LineSegments(geometry=BufferGeometry(attributes={'position': BufferAttribute(array=array([[0., 0…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "g1 = BufferGeometry(\n",
    "    attributes={\n",
    "        'position': BufferAttribute(np.array([\n",
    "            [0, 0, 0], [1, 1, 1],\n",
    "            [2, 2, 2], [4, 4, 4]\n",
    "        ], dtype=np.float32), normalized=False),\n",
    "        'color': BufferAttribute(np.array([\n",
    "            [1, 0, 0], [1, 0, 0],\n",
    "            [0, 1, 0], [0, 0, 1]\n",
    "        ], dtype=np.float32), normalized=False),\n",
    "    },\n",
    ")\n",
    "m1 = LineBasicMaterial(vertexColors='VertexColors', linewidth=10)\n",
    "line1 = LineSegments(g1, m1)\n",
    "line1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we'll set up two variants of the instance geometry based lines. One with a single color, and one with vertex colors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "pythree_example_model_012",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Preview(child=LineSegments2(geometry=LineSegmentsGeometry(positions=array([[[0., 0., 0.],\n",
       "        [1., 1., 1.]…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "g2 = LineSegmentsGeometry(\n",
    "    positions=[\n",
    "        [[0, 0, 0], [1, 1, 1]],\n",
    "        [[2, 2, 2], [4, 4, 4]]\n",
    "    ],\n",
    ")\n",
    "m2 = LineMaterial(linewidth=10, color='cyan')\n",
    "line2 = LineSegments2(g2, m2)\n",
    "line2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "pythree_example_model_018",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Preview(child=LineSegments2(geometry=LineSegmentsGeometry(colors=array([[[1., 0., 0.],\n",
       "        [1., 0., 0.]],\n",
       "…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "g3 = LineSegmentsGeometry(\n",
    "    positions=[\n",
    "        [[0, 0, 0], [1, 1, 1]],\n",
    "        [[2, 2, 2], [4, 4, 4]]\n",
    "    ],\n",
    "    colors=[\n",
    "        [[1, 0, 0], [1, 0, 0]],\n",
    "        [[0, 1, 0], [0, 0, 1]]\n",
    "    ],\n",
    ")\n",
    "m3 = LineMaterial(linewidth=10, vertexColors='VertexColors')\n",
    "line3 = LineSegments2(g3, m3)\n",
    "line3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, let's set up a simple scene and renderer, and add some checkboxes so we can toggle the visibility of the different lines."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "view_width = 600\n",
    "view_height = 400\n",
    "camera = PerspectiveCamera(position=[10, 0, 0], aspect=view_width/view_height)\n",
    "key_light = DirectionalLight(position=[0, 10, 10])\n",
    "ambient_light = AmbientLight()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "scene = Scene(children=[line1, line2, line3, camera, key_light, ambient_light])\n",
    "controller = OrbitControls(controlling=camera, screenSpacePanning=False)\n",
    "renderer = Renderer(camera=camera, scene=scene, controls=[controller],\n",
    "                    width=view_width, height=view_height)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "pythree_example_model_046",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "VBox(children=(Renderer(camera=PerspectiveCamera(aspect=1.5, position=(10.0, 0.0, 0.0), projectionMatrix=(1.42…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "chks = [\n",
    "    Checkbox(True, description='GL line'),\n",
    "    Checkbox(True, description='Fat line (single color)'),\n",
    "    Checkbox(True, description='Fat line (vertex colors)'),\n",
    "]\n",
    "jslink((chks[0], 'value'), (line1, 'visible'))\n",
    "jslink((chks[1], 'value'), (line2, 'visible'))\n",
    "jslink((chks[2], 'value'), (line3, 'visible'))\n",
    "VBox([renderer, HBox(chks)])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For reference, the code below shows how you would recreate the line geometry and material from the kernel. The only significant difference is that you need to declare the render view resolution on material creation, while the included `LineMaterial` automatically sets this."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "# The line segment points and colors.\n",
    "# Each array of six is one instance/segment [x1, y1, z1, x2, y2, z2]\n",
    "posInstBuffer = InstancedInterleavedBuffer( np.array([\n",
    "    [0, 0, 0, 1, 1, 1],\n",
    "    [2, 2, 2, 4, 4, 4]\n",
    "], dtype=np.float32))\n",
    "colInstBuffer = InstancedInterleavedBuffer( np.array([\n",
    "    [1, 0, 0, 1, 0, 0],\n",
    "    [0, 1, 0, 0, 0, 1]\n",
    "], dtype=np.float32))\n",
    "\n",
    "# This uses InstancedBufferGeometry, so that the geometry is reused for each line segment\n",
    "lineGeo = InstancedBufferGeometry(attributes={\n",
    "    # Helper line geometry (2x4 grid), that is instanced\n",
    "    'position': BufferAttribute(np.array([\n",
    "        [ 1,  2, 0], [1,  2, 0],\n",
    "        [-1,  1, 0], [1,  1, 0],\n",
    "        [-1,  0, 0], [1,  0, 0],\n",
    "        [-1, -1, 0], [1, -1, 0]\n",
    "    ], dtype=np.float32)),\n",
    "    'uv': BufferAttribute(np.array([\n",
    "        [-1,  2], [1,  2],\n",
    "        [-1,  1], [1,  1],\n",
    "        [-1, -1], [1, -1],\n",
    "        [-1, -2], [1, -2]\n",
    "    ], dtype=np.float32)),\n",
    "    'index': BufferAttribute(np.array([\n",
    "        0, 2, 1,\n",
    "        2, 3, 1,\n",
    "        2, 4, 3,\n",
    "        4, 5, 3,\n",
    "        4, 6, 5,\n",
    "        6, 7, 5\n",
    "    ], dtype=np.uint8)),\n",
    "    # The line segments are split into start/end for each instance:\n",
    "    'instanceStart': InterleavedBufferAttribute(posInstBuffer, 3, 0),\n",
    "    'instanceEnd': InterleavedBufferAttribute(posInstBuffer, 3, 3),\n",
    "    'instanceColorStart': InterleavedBufferAttribute(colInstBuffer, 3, 0),\n",
    "    'instanceColorEnd': InterleavedBufferAttribute(colInstBuffer, 3, 3),\n",
    "})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "# The line material shader:\n",
    "lineMat = ShaderMaterial(\n",
    "    vertexShader='''\n",
    "#include <common>\n",
    "#include <color_pars_vertex>\n",
    "#include <fog_pars_vertex>\n",
    "#include <logdepthbuf_pars_vertex>\n",
    "#include <clipping_planes_pars_vertex>\n",
    "\n",
    "uniform float linewidth;\n",
    "uniform vec2 resolution;\n",
    "\n",
    "attribute vec3 instanceStart;\n",
    "attribute vec3 instanceEnd;\n",
    "\n",
    "attribute vec3 instanceColorStart;\n",
    "attribute vec3 instanceColorEnd;\n",
    "\n",
    "varying vec2 vUv;\n",
    "\n",
    "void trimSegment( const in vec4 start, inout vec4 end ) {\n",
    "\n",
    "    // trim end segment so it terminates between the camera plane and the near plane\n",
    "\n",
    "    // conservative estimate of the near plane\n",
    "    float a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column\n",
    "    float b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column\n",
    "    float nearEstimate = - 0.5 * b / a;\n",
    "\n",
    "    float alpha = ( nearEstimate - start.z ) / ( end.z - start.z );\n",
    "\n",
    "    end.xyz = mix( start.xyz, end.xyz, alpha );\n",
    "\n",
    "}\n",
    "\n",
    "void main() {\n",
    "\n",
    "    #ifdef USE_COLOR\n",
    "\n",
    "        vColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd;\n",
    "\n",
    "    #endif\n",
    "    \n",
    "    float aspect = resolution.x / resolution.y;\n",
    "\n",
    "    vUv = uv;\n",
    "    \n",
    "    // camera space\n",
    "    vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );\n",
    "    vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );\n",
    "\n",
    "    // special case for perspective projection, and segments that terminate either in, or behind, the camera plane\n",
    "    // clearly the gpu firmware has a way of addressing this issue when projecting into ndc space\n",
    "    // but we need to perform ndc-space calculations in the shader, so we must address this issue directly\n",
    "    // perhaps there is a more elegant solution -- WestLangley\n",
    "\n",
    "    bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column\n",
    "\n",
    "    if ( perspective ) {\n",
    "\n",
    "        if ( start.z < 0.0 && end.z >= 0.0 ) {\n",
    "\n",
    "            trimSegment( start, end );\n",
    "\n",
    "        } else if ( end.z < 0.0 && start.z >= 0.0 ) {\n",
    "\n",
    "            trimSegment( end, start );\n",
    "\n",
    "        }\n",
    "\n",
    "    }\n",
    "\n",
    "    // clip space\n",
    "    vec4 clipStart = projectionMatrix * start;\n",
    "    vec4 clipEnd = projectionMatrix * end;\n",
    "\n",
    "    // ndc space\n",
    "    vec2 ndcStart = clipStart.xy / clipStart.w;\n",
    "    vec2 ndcEnd = clipEnd.xy / clipEnd.w;\n",
    "\n",
    "    // direction\n",
    "    vec2 dir = ndcEnd - ndcStart;\n",
    "\n",
    "    // account for clip-space aspect ratio\n",
    "    dir.x *= aspect;\n",
    "    dir = normalize( dir );\n",
    "\n",
    "    // perpendicular to dir\n",
    "    vec2 offset = vec2( dir.y, - dir.x );\n",
    "\n",
    "    // undo aspect ratio adjustment\n",
    "    dir.x /= aspect;\n",
    "    offset.x /= aspect;\n",
    "\n",
    "    // sign flip\n",
    "    if ( position.x < 0.0 ) offset *= - 1.0;\n",
    "\n",
    "    // endcaps\n",
    "    if ( position.y < 0.0 ) {\n",
    "\n",
    "        offset += - dir;\n",
    "\n",
    "    } else if ( position.y > 1.0 ) {\n",
    "\n",
    "        offset += dir;\n",
    "\n",
    "    }\n",
    "\n",
    "    // adjust for linewidth\n",
    "    offset *= linewidth;\n",
    "    \n",
    "    // adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ...\n",
    "    offset /= resolution.y;\n",
    "\n",
    "    // select end\n",
    "    vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd;\n",
    "\n",
    "    // back to clip space\n",
    "    offset *= clip.w;\n",
    "\n",
    "    clip.xy += offset;\n",
    "\n",
    "    gl_Position = clip;\n",
    "    \n",
    "    vec4 mvPosition = ( position.y < 0.5 ) ? start : end; // this is an approximation\n",
    "\n",
    "    #include <logdepthbuf_vertex>\n",
    "    #include <clipping_planes_vertex>\n",
    "    #include <fog_vertex>\n",
    "}\n",
    "''',\n",
    "    fragmentShader='''\n",
    "uniform vec3 diffuse;\n",
    "uniform float opacity;\n",
    "\n",
    "varying float vLineDistance;\n",
    "\n",
    "#include <common>\n",
    "#include <color_pars_fragment>\n",
    "#include <fog_pars_fragment>\n",
    "#include <logdepthbuf_pars_fragment>\n",
    "#include <clipping_planes_pars_fragment>\n",
    "\n",
    "varying vec2 vUv;\n",
    "\n",
    "void main() {\n",
    "\n",
    "    #include <clipping_planes_fragment>\n",
    "\n",
    "\n",
    "    if ( abs( vUv.y ) > 1.0 ) {\n",
    "\n",
    "        float a = vUv.x;\n",
    "        float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;\n",
    "        float len2 = a * a + b * b;\n",
    "\n",
    "        if ( len2 > 1.0 ) discard;\n",
    "\n",
    "    }\n",
    "\n",
    "    vec4 diffuseColor = vec4( diffuse, opacity );\n",
    "\n",
    "    #include <logdepthbuf_fragment>\n",
    "    #include <color_fragment>\n",
    "\n",
    "    gl_FragColor = vec4( diffuseColor.rgb, diffuseColor.a );\n",
    "\n",
    "    #include <premultiplied_alpha_fragment>\n",
    "    #include <tonemapping_fragment>\n",
    "    #include <encodings_fragment>\n",
    "    #include <fog_fragment>\n",
    "\n",
    "}\n",
    "''',\n",
    "    vertexColors='VertexColors',\n",
    "    uniforms=dict(\n",
    "        linewidth={'value': 10.0},\n",
    "        resolution={'value': (100., 100.)},\n",
    "        **UniformsLib['common']\n",
    "    )\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "pythree_example_model_060",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Preview(child=Mesh(geometry=InstancedBufferGeometry(attributes={'position': BufferAttribute(array=array([[ 1.,…"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "Mesh(lineGeo, lineMat)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.6"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {
     "2108202c-c417-468e-a67f-4c8422eb49e2": {
      "model_module": "jupyter-threejs",
      "model_module_version": "2.0.0",
      "model_name": "OrthographicCameraModel",
      "state": {
       "bottom": -5,
       "far": 500,
       "left": -5,
       "near": 0.5,
       "projectionMatrix": [
        0.2,
        0,
        0,
        0,
        0,
        0.2,
        0,
        0,
        0,
        0,
        -0.004004004004004004,
        0,
        0,
        0,
        -1.002002002002002,
        1
       ],
       "right": 5,
       "top": 5
      }
     },
     "a0da8954-9821-487a-8da1-409e8679c9cc": {
      "model_module": "jupyter-threejs",
      "model_module_version": "2.0.0",
      "model_name": "DirectionalLightShadowModel",
      "state": {
       "camera": "IPY_MODEL_2108202c-c417-468e-a67f-4c8422eb49e2"
      }
     },
     "dedd8574-6920-4f16-bdee-ae7fad1749b1": {
      "model_module": "jupyter-threejs",
      "model_module_version": "2.0.0",
      "model_name": "Object3DModel",
      "state": {}
     },
     "pythree_example_model_001": {
      "buffers": [
       {
        "data": "AAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AAAAQAAAAEAAAABAAACAQAAAgEAAAIBA",
        "encoding": "base64",
        "path": [
         "array",
         "buffer"
        ]
       }
      ],
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "BufferAttributeModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "array": {
        "dtype": "float32",
        "shape": [
         4,
         3
        ]
       },
       "version": 2
      }
     },
     "pythree_example_model_002": {
      "buffers": [
       {
        "data": "AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/",
        "encoding": "base64",
        "path": [
         "array",
         "buffer"
        ]
       }
      ],
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "BufferAttributeModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "array": {
        "dtype": "float32",
        "shape": [
         4,
         3
        ]
       },
       "version": 2
      }
     },
     "pythree_example_model_003": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "BufferGeometryModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "attributes": {
        "color": "IPY_MODEL_pythree_example_model_002",
        "position": "IPY_MODEL_pythree_example_model_001"
       }
      }
     },
     "pythree_example_model_004": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "LineBasicMaterialModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "linewidth": 10,
       "vertexColors": "VertexColors"
      }
     },
     "pythree_example_model_005": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "LineSegmentsModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "geometry": "IPY_MODEL_pythree_example_model_003",
       "material": "IPY_MODEL_pythree_example_model_004"
      }
     },
     "pythree_example_model_006": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "PreviewModel",
      "state": {
       "_alpha": false,
       "_model_module_version": "^2.0.0",
       "_view_module_version": "^2.0.0",
       "child": "IPY_MODEL_pythree_example_model_005",
       "layout": "IPY_MODEL_pythree_example_model_007",
       "shadowMap": "IPY_MODEL_pythree_example_model_008"
      }
     },
     "pythree_example_model_007": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.1.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "pythree_example_model_008": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "WebGLShadowMapModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": ""
      }
     },
     "pythree_example_model_009": {
      "buffers": [
       {
        "data": "AAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AAAAQAAAAEAAAABAAACAQAAAgEAAAIBA",
        "encoding": "base64",
        "path": [
         "positions",
         "buffer"
        ]
       }
      ],
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "LineSegmentsGeometryModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "positions": {
        "dtype": "float32",
        "shape": [
         2,
         2,
         3
        ]
       }
      }
     },
     "pythree_example_model_010": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "LineMaterialModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "color": "cyan",
       "linewidth": 10
      }
     },
     "pythree_example_model_011": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "LineSegments2Model",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "geometry": "IPY_MODEL_pythree_example_model_009",
       "material": "IPY_MODEL_pythree_example_model_010",
       "morphTargetInfluences": []
      }
     },
     "pythree_example_model_012": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "PreviewModel",
      "state": {
       "_alpha": false,
       "_model_module_version": "^2.0.0",
       "_view_module_version": "^2.0.0",
       "child": "IPY_MODEL_pythree_example_model_011",
       "layout": "IPY_MODEL_pythree_example_model_013",
       "shadowMap": "IPY_MODEL_pythree_example_model_014"
      }
     },
     "pythree_example_model_013": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.1.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "pythree_example_model_014": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "WebGLShadowMapModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": ""
      }
     },
     "pythree_example_model_015": {
      "buffers": [
       {
        "data": "AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/",
        "encoding": "base64",
        "path": [
         "colors",
         "buffer"
        ]
       },
       {
        "data": "AAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AAAAQAAAAEAAAABAAACAQAAAgEAAAIBA",
        "encoding": "base64",
        "path": [
         "positions",
         "buffer"
        ]
       }
      ],
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "LineSegmentsGeometryModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "colors": {
        "dtype": "float32",
        "shape": [
         2,
         2,
         3
        ]
       },
       "positions": {
        "dtype": "float32",
        "shape": [
         2,
         2,
         3
        ]
       }
      }
     },
     "pythree_example_model_016": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "LineMaterialModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "linewidth": 10,
       "vertexColors": "VertexColors"
      }
     },
     "pythree_example_model_017": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "LineSegments2Model",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "geometry": "IPY_MODEL_pythree_example_model_015",
       "material": "IPY_MODEL_pythree_example_model_016",
       "morphTargetInfluences": []
      }
     },
     "pythree_example_model_018": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "PreviewModel",
      "state": {
       "_alpha": false,
       "_model_module_version": "^2.0.0",
       "_view_module_version": "^2.0.0",
       "child": "IPY_MODEL_pythree_example_model_017",
       "layout": "IPY_MODEL_pythree_example_model_019",
       "shadowMap": "IPY_MODEL_pythree_example_model_020"
      }
     },
     "pythree_example_model_019": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.1.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "pythree_example_model_020": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "WebGLShadowMapModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": ""
      }
     },
     "pythree_example_model_021": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "PerspectiveCameraModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "aspect": 1.5,
       "matrix": [
        1,
        0,
        0,
        0,
        0,
        1,
        0,
        0,
        0,
        0,
        1,
        0,
        10,
        0,
        0,
        1
       ],
       "position": [
        10,
        0,
        0
       ],
       "projectionMatrix": [
        1.4296712803397058,
        0,
        0,
        0,
        0,
        2.1445069205095586,
        0,
        0,
        0,
        0,
        -1.00010000500025,
        -1,
        0,
        0,
        -0.200010000500025,
        0
       ]
      }
     },
     "pythree_example_model_022": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "DirectionalLightModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "matrixWorldNeedsUpdate": true,
       "position": [
        0,
        10,
        10
       ],
       "shadow": "IPY_MODEL_a0da8954-9821-487a-8da1-409e8679c9cc",
       "target": "IPY_MODEL_dedd8574-6920-4f16-bdee-ae7fad1749b1"
      }
     },
     "pythree_example_model_023": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "AmbientLightModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": ""
      }
     },
     "pythree_example_model_027": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "SceneModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "children": [
        "IPY_MODEL_pythree_example_model_005",
        "IPY_MODEL_pythree_example_model_011",
        "IPY_MODEL_pythree_example_model_017",
        "IPY_MODEL_pythree_example_model_021",
        "IPY_MODEL_pythree_example_model_022",
        "IPY_MODEL_pythree_example_model_023"
       ]
      }
     },
     "pythree_example_model_028": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "OrbitControlsModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "controlling": "IPY_MODEL_pythree_example_model_021",
       "maxAzimuthAngle": "inf",
       "maxDistance": "inf",
       "maxZoom": "inf",
       "minAzimuthAngle": "-inf",
       "screenSpacePanning": false
      }
     },
     "pythree_example_model_029": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "RendererModel",
      "state": {
       "_height": 400,
       "_model_module_version": "^2.0.0",
       "_view_module_version": "^2.0.0",
       "_width": 600,
       "camera": "IPY_MODEL_pythree_example_model_021",
       "controls": [
        "IPY_MODEL_pythree_example_model_028"
       ],
       "layout": "IPY_MODEL_pythree_example_model_030",
       "scene": "IPY_MODEL_pythree_example_model_027",
       "shadowMap": "IPY_MODEL_pythree_example_model_031"
      }
     },
     "pythree_example_model_030": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.1.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "pythree_example_model_031": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "WebGLShadowMapModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": ""
      }
     },
     "pythree_example_model_032": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.3.0",
      "model_name": "CheckboxModel",
      "state": {
       "description": "GL line",
       "disabled": false,
       "layout": "IPY_MODEL_pythree_example_model_033",
       "style": "IPY_MODEL_pythree_example_model_034",
       "value": true
      }
     },
     "pythree_example_model_033": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.1.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "pythree_example_model_034": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.3.0",
      "model_name": "DescriptionStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "pythree_example_model_035": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.3.0",
      "model_name": "CheckboxModel",
      "state": {
       "description": "Fat line (single color)",
       "disabled": false,
       "layout": "IPY_MODEL_pythree_example_model_036",
       "style": "IPY_MODEL_pythree_example_model_037",
       "value": true
      }
     },
     "pythree_example_model_036": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.1.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "pythree_example_model_037": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.3.0",
      "model_name": "DescriptionStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "pythree_example_model_038": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.3.0",
      "model_name": "CheckboxModel",
      "state": {
       "description": "Fat line (vertex colors)",
       "disabled": false,
       "layout": "IPY_MODEL_pythree_example_model_039",
       "style": "IPY_MODEL_pythree_example_model_040",
       "value": true
      }
     },
     "pythree_example_model_039": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.1.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "pythree_example_model_040": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.3.0",
      "model_name": "DescriptionStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "pythree_example_model_041": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.3.0",
      "model_name": "LinkModel",
      "state": {
       "source": [
        "IPY_MODEL_pythree_example_model_032",
        "value"
       ],
       "target": [
        "IPY_MODEL_pythree_example_model_005",
        "visible"
       ]
      }
     },
     "pythree_example_model_042": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.3.0",
      "model_name": "LinkModel",
      "state": {
       "source": [
        "IPY_MODEL_pythree_example_model_035",
        "value"
       ],
       "target": [
        "IPY_MODEL_pythree_example_model_011",
        "visible"
       ]
      }
     },
     "pythree_example_model_043": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.3.0",
      "model_name": "LinkModel",
      "state": {
       "source": [
        "IPY_MODEL_pythree_example_model_038",
        "value"
       ],
       "target": [
        "IPY_MODEL_pythree_example_model_017",
        "visible"
       ]
      }
     },
     "pythree_example_model_044": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.3.0",
      "model_name": "HBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_pythree_example_model_032",
        "IPY_MODEL_pythree_example_model_035",
        "IPY_MODEL_pythree_example_model_038"
       ],
       "layout": "IPY_MODEL_pythree_example_model_045"
      }
     },
     "pythree_example_model_045": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.1.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "pythree_example_model_046": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "1.3.0",
      "model_name": "VBoxModel",
      "state": {
       "children": [
        "IPY_MODEL_pythree_example_model_029",
        "IPY_MODEL_pythree_example_model_044"
       ],
       "layout": "IPY_MODEL_pythree_example_model_047"
      }
     },
     "pythree_example_model_047": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.1.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "pythree_example_model_048": {
      "buffers": [
       {
        "data": "AAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AAAAQAAAAEAAAABAAACAQAAAgEAAAIBA",
        "encoding": "base64",
        "path": [
         "array",
         "buffer"
        ]
       }
      ],
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "InstancedInterleavedBufferModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "array": {
        "dtype": "float32",
        "shape": [
         2,
         6
        ]
       },
       "version": 2
      }
     },
     "pythree_example_model_049": {
      "buffers": [
       {
        "data": "AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAAAAAAIA/",
        "encoding": "base64",
        "path": [
         "array",
         "buffer"
        ]
       }
      ],
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "InstancedInterleavedBufferModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "array": {
        "dtype": "float32",
        "shape": [
         2,
         6
        ]
       },
       "version": 2
      }
     },
     "pythree_example_model_050": {
      "buffers": [
       {
        "data": "AACAPwAAAEAAAAAAAACAPwAAAEAAAAAAAACAvwAAgD8AAAAAAACAPwAAgD8AAAAAAACAvwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAgL8AAAAAAACAPwAAgL8AAAAA",
        "encoding": "base64",
        "path": [
         "array",
         "buffer"
        ]
       }
      ],
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "BufferAttributeModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "array": {
        "dtype": "float32",
        "shape": [
         8,
         3
        ]
       },
       "normalized": true,
       "version": 2
      }
     },
     "pythree_example_model_051": {
      "buffers": [
       {
        "data": "AACAvwAAAEAAAIA/AAAAQAAAgL8AAIA/AACAPwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAAMAAAIA/AAAAwA==",
        "encoding": "base64",
        "path": [
         "array",
         "buffer"
        ]
       }
      ],
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "BufferAttributeModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "array": {
        "dtype": "float32",
        "shape": [
         8,
         2
        ]
       },
       "normalized": true,
       "version": 2
      }
     },
     "pythree_example_model_052": {
      "buffers": [
       {
        "data": "AAIBAgMBAgQDBAUDBAYFBgcF",
        "encoding": "base64",
        "path": [
         "array",
         "buffer"
        ]
       }
      ],
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "BufferAttributeModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "array": {
        "dtype": "uint8",
        "shape": [
         18
        ]
       },
       "normalized": true,
       "version": 2
      }
     },
     "pythree_example_model_053": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "InterleavedBufferAttributeModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "data": "IPY_MODEL_pythree_example_model_048",
       "itemSize": 3
      }
     },
     "pythree_example_model_054": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "InterleavedBufferAttributeModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "data": "IPY_MODEL_pythree_example_model_048",
       "itemSize": 3,
       "offset": 3
      }
     },
     "pythree_example_model_055": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "InterleavedBufferAttributeModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "data": "IPY_MODEL_pythree_example_model_049",
       "itemSize": 3
      }
     },
     "pythree_example_model_056": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "InterleavedBufferAttributeModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "data": "IPY_MODEL_pythree_example_model_049",
       "itemSize": 3,
       "offset": 3
      }
     },
     "pythree_example_model_057": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "InstancedBufferGeometryModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "attributes": {
        "index": "IPY_MODEL_pythree_example_model_052",
        "instanceColorEnd": "IPY_MODEL_pythree_example_model_056",
        "instanceColorStart": "IPY_MODEL_pythree_example_model_055",
        "instanceEnd": "IPY_MODEL_pythree_example_model_054",
        "instanceStart": "IPY_MODEL_pythree_example_model_053",
        "position": "IPY_MODEL_pythree_example_model_050",
        "uv": "IPY_MODEL_pythree_example_model_051"
       }
      }
     },
     "pythree_example_model_058": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "ShaderMaterialModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "fragmentShader": "\nuniform vec3 diffuse;\nuniform float opacity;\n\nvarying float vLineDistance;\n\n#include <common>\n#include <color_pars_fragment>\n#include <fog_pars_fragment>\n#include <logdepthbuf_pars_fragment>\n#include <clipping_planes_pars_fragment>\n\nvarying vec2 vUv;\n\nvoid main() {\n\n    #include <clipping_planes_fragment>\n\n\n    if ( abs( vUv.y ) > 1.0 ) {\n\n        float a = vUv.x;\n        float b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;\n        float len2 = a * a + b * b;\n\n        if ( len2 > 1.0 ) discard;\n\n    }\n\n    vec4 diffuseColor = vec4( diffuse, opacity );\n\n    #include <logdepthbuf_fragment>\n    #include <color_fragment>\n\n    gl_FragColor = vec4( diffuseColor.rgb, diffuseColor.a );\n\n    #include <premultiplied_alpha_fragment>\n    #include <tonemapping_fragment>\n    #include <encodings_fragment>\n    #include <fog_fragment>\n\n}\n",
       "uniforms": {
        "alphaMap": {
         "value": null
        },
        "diffuse": {
         "type": "c",
         "value": "#eeeeee"
        },
        "linewidth": {
         "value": 10
        },
        "map": {
         "value": null
        },
        "opacity": {
         "value": 1
        },
        "resolution": {
         "type": "v2",
         "value": [
          100,
          100
         ]
        },
        "uvTransform": {
         "type": "m3",
         "value": [
          1,
          0,
          0,
          0,
          1,
          0,
          0,
          0,
          1
         ]
        }
       },
       "vertexColors": "VertexColors",
       "vertexShader": "\n#include <common>\n#include <color_pars_vertex>\n#include <fog_pars_vertex>\n#include <logdepthbuf_pars_vertex>\n#include <clipping_planes_pars_vertex>\n\nuniform float linewidth;\nuniform vec2 resolution;\n\nattribute vec3 instanceStart;\nattribute vec3 instanceEnd;\n\nattribute vec3 instanceColorStart;\nattribute vec3 instanceColorEnd;\n\nvarying vec2 vUv;\n\nvoid trimSegment( const in vec4 start, inout vec4 end ) {\n\n    // trim end segment so it terminates between the camera plane and the near plane\n\n    // conservative estimate of the near plane\n    float a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column\n    float b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column\n    float nearEstimate = - 0.5 * b / a;\n\n    float alpha = ( nearEstimate - start.z ) / ( end.z - start.z );\n\n    end.xyz = mix( start.xyz, end.xyz, alpha );\n\n}\n\nvoid main() {\n\n    #ifdef USE_COLOR\n\n        vColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd;\n\n    #endif\n    \n    float aspect = resolution.x / resolution.y;\n\n    vUv = uv;\n    \n    // camera space\n    vec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );\n    vec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );\n\n    // special case for perspective projection, and segments that terminate either in, or behind, the camera plane\n    // clearly the gpu firmware has a way of addressing this issue when projecting into ndc space\n    // but we need to perform ndc-space calculations in the shader, so we must address this issue directly\n    // perhaps there is a more elegant solution -- WestLangley\n\n    bool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column\n\n    if ( perspective ) {\n\n        if ( start.z < 0.0 && end.z >= 0.0 ) {\n\n            trimSegment( start, end );\n\n        } else if ( end.z < 0.0 && start.z >= 0.0 ) {\n\n            trimSegment( end, start );\n\n        }\n\n    }\n\n    // clip space\n    vec4 clipStart = projectionMatrix * start;\n    vec4 clipEnd = projectionMatrix * end;\n\n    // ndc space\n    vec2 ndcStart = clipStart.xy / clipStart.w;\n    vec2 ndcEnd = clipEnd.xy / clipEnd.w;\n\n    // direction\n    vec2 dir = ndcEnd - ndcStart;\n\n    // account for clip-space aspect ratio\n    dir.x *= aspect;\n    dir = normalize( dir );\n\n    // perpendicular to dir\n    vec2 offset = vec2( dir.y, - dir.x );\n\n    // undo aspect ratio adjustment\n    dir.x /= aspect;\n    offset.x /= aspect;\n\n    // sign flip\n    if ( position.x < 0.0 ) offset *= - 1.0;\n\n    // endcaps\n    if ( position.y < 0.0 ) {\n\n        offset += - dir;\n\n    } else if ( position.y > 1.0 ) {\n\n        offset += dir;\n\n    }\n\n    // adjust for linewidth\n    offset *= linewidth;\n    \n    // adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ...\n    offset /= resolution.y;\n\n    // select end\n    vec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd;\n\n    // back to clip space\n    offset *= clip.w;\n\n    clip.xy += offset;\n\n    gl_Position = clip;\n    \n    vec4 mvPosition = ( position.y < 0.5 ) ? start : end; // this is an approximation\n\n    #include <logdepthbuf_vertex>\n    #include <clipping_planes_vertex>\n    #include <fog_vertex>\n}\n"
      }
     },
     "pythree_example_model_059": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "MeshModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": "",
       "geometry": "IPY_MODEL_pythree_example_model_057",
       "material": "IPY_MODEL_pythree_example_model_058",
       "morphTargetInfluences": []
      }
     },
     "pythree_example_model_060": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "PreviewModel",
      "state": {
       "_alpha": false,
       "_model_module_version": "^2.0.0",
       "_view_module_version": "^2.0.0",
       "child": "IPY_MODEL_pythree_example_model_059",
       "layout": "IPY_MODEL_pythree_example_model_061",
       "shadowMap": "IPY_MODEL_pythree_example_model_062"
      }
     },
     "pythree_example_model_061": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "1.1.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "pythree_example_model_062": {
      "model_module": "jupyter-threejs",
      "model_module_version": "^2.0.0",
      "model_name": "WebGLShadowMapModel",
      "state": {
       "_model_module_version": "^2.0.0",
       "_view_module": null,
       "_view_module_version": ""
      }
     }
    },
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
